package org.zh.test;

import com.alibaba.druid.DbType;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.druid.util.JdbcUtils;
import com.alibaba.druid.util.MySqlUtils;
import com.mysql.cj.MysqlType;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class CreateClassFile {


    public static void createClass(Connection conn, String tableName) throws SQLException, IOException {
        String sql = "select COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT,COLUMN_TYPE,COLUMN_KEY,EXTRA FROM INFORMATION_SCHEMA.COLUMNS WHERE  TABLE_NAME = '" + tableName + "'";
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(sql);
        List<ColumnInfo> list = new ArrayList<>();
        //class
        String camelCase = getCamelCase(tableName);
        String clazzName = camelCase.substring(0, 1).toUpperCase() + camelCase.substring(1);

        while (rs.next()) {
            ColumnInfo ci = new ColumnInfo(
                    rs.getString("COLUMN_NAME"),
                    rs.getString("DATA_TYPE"),
                    rs.getString("COLUMN_COMMENT"),
                    rs.getString("COLUMN_TYPE"),
                    rs.getString("COLUMN_KEY"),
                    rs.getString("EXTRA")
            );
            list.add(ci);
        }
        rs.close();
        stmt.close();
        conn.close();
        //  create file
        String dir = System.getProperty("user.dir");
        Path path = Paths.get(dir, "clazz", clazzName + ".java");
        Files.deleteIfExists(path);
        Files.createDirectories(path.getParent());
        BufferedWriter bw = Files.newBufferedWriter(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
        //create class
        StringBuffer sb = new StringBuffer();
        sb.append("\n")
                .append(improtString(list))
                .append("@Table(\"").append(tableName).append("\")")
                .append("public class ")
                .append(clazzName)
                .append(" {\n");
        list.forEach(columnInfo -> sb.append("/**\n")
                .append("*")
                .append(columnInfo.columnComment())
                .append("--->")
                .append(columnInfo.dataType())
                .append("--->")
                .append(columnInfo.columnName())
                .append("--->")
                .append(columnInfo.columnType())
                .append("--->")
                .append(columnInfo.columnKey())
                .append("--->")
                .append(columnInfo.extra())
                .append("\n*/\n")
                .append("private ")
                .append(getJavaTypeShort(columnInfo.columnType()))
                .append("  ")
                .append(getCamelCase(columnInfo.columnName()))
                .append(";"));
        sb.append(getterSetter(list))
                .append(getToString(clazzName, list))
                .append(getEquals(tableName, list))
                .append(getHashCode(list))
                .append(beanToMapString(tableName, list));
        sb.append("\n\n}");
        bw.write(sb.toString());
        bw.flush();
        bw.close();
    }

    public static String beanToMapString(String tableName) {
        String camelCase = getCamelCase(tableName);
        String clazzName = camelCase.substring(0, 1).toUpperCase() + camelCase.substring(1);
        return "public static Map<String, Object> beanToMap(" + clazzName + " " + camelCase + ") {" +
               "Map<String, Object> map = new HashMap<>();" + "Class<" + clazzName + "> tc = " + clazzName + ".class;" +
               "try{ MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(tc, MethodHandles.lookup());Field[] fields = tc.getDeclaredFields();" +
               "for (Field field : fields) {" +
               " String fileName = field.getName();" +
               " String columnName = TCCache.getFileOrColumn(tc, fileName);" +
               "MethodHandle getter = lookup.findGetter(tc, fileName, field.getType());Object o = getter.invoke(" +
               camelCase +
               ");" +
               "   if(o!=null){  map.put(columnName, o);}}}catch (Throwable e) {throw new RuntimeException(e);}return map; }";
    }

    public static String beanToMapString(String tableName, List<ColumnInfo> list) {
        String camelCase = getCamelCase(tableName);
        String clazzName = getCamelCase1(camelCase);
        StringBuilder sb = new StringBuilder();
        sb.append("public static Map<String, Object> beanToMap(").append(clazzName).append(" ").append(camelCase).append(") {")
                .append("Map<String, Object> map = new HashMap<>();").append("Class<").append(clazzName).append("> tc = ").append(clazzName).append(".class;");
        for (ColumnInfo info : list) {
            String cCase = getCamelCase(info.columnName());
            String cCase1 = getCamelCase1(cCase);
            if (getJavaTypeShort(info.columnType()).equals("String")) {
                sb.append("if(StringUtils.hasText(").append(camelCase).append(".get").append(cCase1).append("())){\n")
                        .append("map.put(TCCache.getFileOrColumn(tc,\"").append(cCase).append("\"),").append(camelCase).append(".get").append(cCase1).append("());}");
            } else {
                sb.append("if(").append(camelCase).append(".get").append(cCase1).append("() != null){\n")
                        .append("map.put(TCCache.getFileOrColumn(tc,\"").append(cCase).append("\"),").append(camelCase).append(".get").append(cCase1).append("());}");
            }
        }
        sb.append("return map;}");
        return sb.toString();
    }

    public static String getterSetter(List<ColumnInfo> list) {
        StringBuffer sb = new StringBuffer();
        list.forEach(ci -> {
            String camelCase = getCamelCase(ci.columnName());
            String cc = camelCase.substring(0, 1).toUpperCase() + camelCase.substring(1);
            sb.append("public ").append(getJavaTypeShort(ci.columnType())).append(" get")
                    .append(cc).append("()").append("{").append(" return ").append(camelCase).append(";").append("}\n")
                    .append("public void set").append(cc).append("(").append(getJavaTypeShort(ci.columnType())).append(" ").append(camelCase)
                    .append(")").append("{").append("this.").append(camelCase).append("=").append(camelCase).append(";").append("}\n");
        });
        return sb.toString();
    }

    public static String getToString(String className, List<ColumnInfo> list) {
        String aaa = "\"";
        String ddd = "=\" + ";
        String ppp = "='\" + ";
        String mmm = "\", ";
        String nnn = " + '\\'' +";
        StringBuilder sb = new StringBuilder();
        sb.append("@Override\n").append(" public String toString() { return ");
        sb.append(aaa).append(className).append("{").append(aaa).append("+");
        for (int i = 0; i < list.size(); i++) {
            ColumnInfo ci = list.get(i);
            String camelCase = getCamelCase(ci.columnName());
            if (i == 0) {
                if (MysqlType.getByName(ci.columnType()).getClassName().equals(String.class.getName())) {
                    sb.append(aaa).append(camelCase).append(ppp).append(camelCase).append(nnn);
                } else {
                    sb.append(aaa).append(camelCase).append(ddd).append(camelCase).append("+");
                }
            } else {
                if (MysqlType.getByName(ci.columnType()).getClassName().equals(String.class.getName())) {
                    sb.append(mmm).append(camelCase).append(ppp).append(camelCase).append(nnn);
                } else {
                    sb.append(mmm).append(camelCase).append(ddd).append(camelCase).append("+");
                }
            }
        }
        sb.append("'}';").append("}");
        return sb.toString();
    }

    public static String getEquals(String tableName, List<ColumnInfo> list) {
        String camelCase = getCamelCase(tableName);
        String clazzName = camelCase.substring(0, 1).toUpperCase() + camelCase.substring(1);
        StringBuilder sb = new StringBuilder();
        sb.append("@Override\n").append(" public boolean equals(Object o) {\n").append("if (this == o) return true;\n").append(" if (o == null || getClass() != o.getClass()) return false;\n");
        sb.append(clazzName).append("  ").append(camelCase).append(" = ").append("(").append(clazzName).append(")").append(" o;\n");
        sb.append("return ");
        for (int i = 0; i < list.size(); i++) {
            ColumnInfo ci = list.get(i);
            String cc = getCamelCase(ci.columnName());
            if (i == list.size() - 1) {
                sb.append("Objects.equals(").append(cc).append(", ").append(camelCase).append(".").append(cc).append(");");
            } else {
                sb.append("Objects.equals(").append(cc).append(", ").append(camelCase).append(".").append(cc).append(") && ");
            }
        }
        sb.append("\n}");
        return sb.toString();
    }

    public static String getHashCode(List<ColumnInfo> list) {
        StringBuilder sb = new StringBuilder();
        sb.append("@Override\n").append(" public int hashCode() {")
                .append("return Objects.hash(");
        for (int i = 0; i < list.size(); i++) {
            ColumnInfo ci = list.get(i);
            sb.append(getCamelCase(ci.columnName())).append(", ");
            if (i == list.size() - 1) {
                sb.append(getCamelCase(ci.columnName())).append(");");
            }
        }
        sb.append("\n}");
        return sb.toString();
    }

    public static String getCamelCase(String column) {
        String[] words = column.split("_");
        StringBuilder camelCase = new StringBuilder();
        for (int i = 0; i < words.length; i++) {
            if (i == 0) {
                camelCase.append(words[i]);
            } else {
                camelCase.append(Character.toUpperCase(words[i].charAt(0)));
                if (words[i].length() > 1) {
                    camelCase.append(words[i].substring(1));
                }
            }
        }
        return camelCase.toString();
    }

    public static String getCamelCase1(String camelCase) {
        return camelCase.substring(0, 1).toUpperCase() + camelCase.substring(1);
    }

    public static String improtString(List<ColumnInfo> list) {
        Set<Class<?>> SIMPLE_TYPE_SET = new HashSet<>();
        SIMPLE_TYPE_SET.add(String.class);
        SIMPLE_TYPE_SET.add(Byte.class);
        SIMPLE_TYPE_SET.add(Short.class);
        SIMPLE_TYPE_SET.add(Character.class);
        SIMPLE_TYPE_SET.add(Integer.class);
        SIMPLE_TYPE_SET.add(Long.class);
        SIMPLE_TYPE_SET.add(Float.class);
        SIMPLE_TYPE_SET.add(Double.class);
        SIMPLE_TYPE_SET.add(Boolean.class);
        SIMPLE_TYPE_SET.add(Class.class);
        SIMPLE_TYPE_SET.add(Object.class);
        SIMPLE_TYPE_SET.add(Exception.class);
        SIMPLE_TYPE_SET.add(Thread.class);
        SIMPLE_TYPE_SET.add(RuntimeException.class);
        StringBuffer sb = new StringBuffer();
        list.forEach(columnInfo -> {
            MysqlType mysqlType = MysqlType.getByName(columnInfo.columnType());
            try {
                Class<?> aClass = Class.forName(mysqlType.getClassName());
                if (!SIMPLE_TYPE_SET.contains(aClass)) {
                    sb.append("import ")
                            .append(aClass.getTypeName()).append(";\n");
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        });
        sb.append("import java.util.Objects;\n")
                .append("import org.zh.client.jdbc.Table;")
                .append("import org.zh.client.jdbc.TCCache;")
                .append("import java.lang.invoke.MethodHandle;")
                .append("import java.lang.invoke.MethodHandles;")
                .append("import java.lang.reflect.Field;")
                .append("import java.util.HashMap;")
                .append("import java.util.Map;")
                .append("import org.springframework.util.StringUtils;");
        return sb.toString();
    }

    private static String getJavaTypeShort(String columnType) {
        String className = MysqlType.getByName(columnType).getClassName();
        String[] split = className.split("\\.");
        return split[split.length - 1];
    }

    public static void main(String[] args) throws SQLException {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/family");
        dataSource.setUsername("zhou");
        dataSource.setPassword("zhouhang");
        DruidPooledConnection conn = dataSource.getConnection();
        String script = JdbcUtils.getCreateTableScript(conn, DbType.mysql);
        System.out.println(script);
        List<String> tables = JdbcUtils.showTables(conn, DbType.mysql);
        for (String ta:tables){
            try {
                createClass(dataSource.getConnection(),ta);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        dataSource.close();
    }

}
